package com.jubaozan.c3.framework.logs.logback;


import static com.jubaozan.c3.framework.logs.UtilElk.LINESEPARATOR;
import static com.jubaozan.c3.framework.logs.UtilElk.put;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.slf4j.Marker;

import com.jubaozan.c3.framework.logs.UtilElk;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.LoggerContextVO;
import ch.qos.logback.classic.spi.StackTraceElementProxy;


import lombok.extern.slf4j.Slf4j;

/**
 * @author jiangjinwu
 * @version 1.0
 * @since 2025/10/7 18:12
 */

@Slf4j

public class UtilLogback {
  private static final String[] EMPTY_ARRAY_STR = new String[0];
  private static String appId;

  private UtilLogback() {}

  public static String getAppId(ILoggingEvent event) {
    if (appId != null) return appId;

    String ret = getLoggerContextValue(event, "springAppName");
    if (ret == null) ret = UtilElk.getAppId();
    if (ret != null) appId = ret;
    return ret;
  }
  private static String getLoggerContextValue(ILoggingEvent event, String name) {
    if (event == null || name == null) return null;

    LoggerContextVO ctx = event.getLoggerContextVO();
    if (ctx == null) return null;

    Map<String, String> props = ctx.getPropertyMap();
    return props == null ? null : props.get(name);
  }

  public static Map<String, Object> toMap(Marker marker) {
    if (marker == null) return null;

    Map<String, Object> map = new HashMap<>();
    put(map, "name", marker.getName());
    return map;
  }

  public static Map<String, Object> toMap(IThrowableProxy thrown) {
    return toMap(thrown, "", "", new HashSet<>(), EMPTY_ARRAY_STR);
  }
  @SuppressWarnings("unchecked")
  private static Map<String, Object> toMap(IThrowableProxy thrown, String caption, String prefix, Set<IThrowableProxy> existThrows, String[] parentTraces) {
    if (thrown == null) return null;

    if (existThrows.contains(thrown)) {
      Map<String, Object> map = new HashMap<>();
      map.put("cirRef", toString(thrown));
      return map;
    }
    existThrows.add(thrown);

    Map<String, Object> map = new HashMap<>();
    map.put("name", thrown.getClassName());
    map.put("message", thrown.getMessage());

    StringBuilder print = new StringBuilder(128).append(prefix).append(caption).append(thrown.getClassName()).append(": ").append(thrown.getMessage()).append(LINESEPARATOR);
    String[] traces = toString(thrown.getStackTraceElementProxyArray());
    if (traces.length > 0) {
      print.ensureCapacity(print.length() + 100 * traces.length);

      int m = traces.length - 1;
      int n = parentTraces.length - 1;
      while (m >= 0 && n >=0 && traces[m].equals(parentTraces[n])) {
        m--; n--;
      }
      int framesInCommon = traces.length - 1 - m;

      for (int i = 0; i <= m; i++) {
        print.append(prefix).append('\t').append(traces[i]).append(LINESEPARATOR);
      }
      if (framesInCommon != 0)print.append(prefix).append("\t... ").append(framesInCommon).append(" more").append(LINESEPARATOR);
    }

    IThrowableProxy[] suppressed = thrown.getSuppressed();
    if (suppressed != null && suppressed.length > 0) {
      map.put("suppressed", true);
      String nextPrefix = prefix + "\t";
      for (IThrowableProxy sup : suppressed) {
        Map<String, Object> tmp = toMap(sup, "Suppressed: ", nextPrefix, existThrows, traces);
        if (tmp != null) {
          print.append(tmp.get("print"));
          Map<String, Object> tmpCause = (Map<String, Object>) tmp.get("cause");
          if (tmpCause != null) print.append(tmpCause.get("print"));
        }
      }
    }
    map.put("print", print.toString());

    put(map, "cause", toMap(thrown.getCause(), "Caused by: ", prefix, existThrows, traces));
    return map;
  }
  private static String[] toString(StackTraceElementProxy[] stacks) {
    if (stacks == null) return new String[0];

    int len = stacks.length;
    String[] ret = new String[len];
    for (int i = 0; i < len; i++) {
      ret[i] = stacks[i].toString();
    }
    return ret;
  }
  private static String toString(IThrowableProxy thrown) {
    if (thrown == null) return null;

    return thrown.getClassName() + "@" + Integer.toHexString(thrown.hashCode()) + " : " + thrown.getMessage();
  }

}
